#include "stdio.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"

#define MARK_FILE(value,flags) ((flags) = (((value)&ALL_FILE)|(flags)))
#define DEL_FILE(value,flags)  ((flags) = ((~(value))&(flags)))
#define MARK_INFO(value,flags) ((flags) = (((value)&ALL_INFO)|(flags)))
#define DEL_INFO(value,flags   ((flags) = ((~(value))&(flags)))


#define FILE_TYPE  0xf8000000
#define OTH_FILE   0x80000000
#define DIR_FILE   0x40000000
#define REG_FILE   0x20000000
#define BAK_FILE   0x10000000
#define DOT_FILE   0x08000000
#define ALL_FILE   0xf8000000

#define STAT_ALL_INFO   0x07f00000
#define STAT_GROUP      0x04000000
#define STAT_OWNER      0x02000000
#define STAT_NUMID      0x01000000
#define STAT_SIZE       0x00800000   //0 present bytes 1 present k or m 
#define STAT_INODE      0x00400000
#define STAT_TIME       0x00200000
#define STAT_PERMISSION 0x00100000

#define STAT_COLOR      0x00080000      
#define STAT_RECUR      0x00040000
#define STAT_HR         0x00020000


void format_permission(struct stat *st)
{
	int mode = st->st_mode
	char buf[12];
	memset(buf,'-',11);
	buf[11]='0';
	switch(mode)
	{
	case S_ISDIR(mode) : buf[0] = 'd'; break;
	case S_ISCHR(mode) : buf[0] = 'c'; break;
	case S_ISBLK(mode) : buf[0] = 'b'; break;
	case S_ISFIFO(mode): buf[0] = 'p'; break;
	case S_ISLNK(mode) : buf[0] = 'l'; break;
	case S_ISSOCK(mode): buf[0] = 's'; break;
	default : break;
	}
	     
    if(S_IRUSR&mode) buf[1] = 'r';
    if(S_IWUSR&mode) buf[2] = 'w';
    if(S_IXUSR&mode) buf[3] = 'x';

    if(S_IRGRP&mode) buf[4] = 'r';
    if(S_IWGRP&mode) buf[5] = 'w';
    if(S_IXGRP&mode) buf[6] = 'x';

    if(S_IROTH&mode) buf[7] = 'r';
    if(S_IWOTH&mode) buf[8] = 'w';
	if(S_IXOTH&mode) buf[9] = 'x';
	
	if(S_ISUID&mode) buf[3] = 's';
	if(S_ISGID&mode) buf[6] = 's';
	if(S_ISVTX&mode) buf[10] = 's'
	printf("%s    ",buf);
}

void format_group(struct stat *st)
{
	int id = st->st_uid;
	struct group *buf = getgruid(id);
	if(buf)
	{
		printf("%-20s    ",buf->gr_name);
	}else
	{
		printf("%-20d    ",id);
	}
}

void format_owener(struct stat *st)
{
	int id = st->st_gid;
	struct passwd *buf_pw = getpwuid(id);
	if(buf)
	{
		printf("%-20s    ",buf_pw->pw_name);
	}else
	{
		printf("%-20d    ",id);
	}
}

void format_time(struct stat *st)
{
	printf("%-15s    ",ctime(&(st->st_ctime)));
}

void format_numid(struct stat *st)
{
	printf("%10s    %10s    ",st->st_uid,st->st_gid);
}


void main(int argc,char *argv)
{
	int flags = 0;
	int *file_index = (int *)malloc(sizeof(int)*argc);
	int files=0;
	for(int i=1;i<argc;i++)
	{
		if( (!strcmp("-a",argv[i])) || (!strcmp("--all",argv[i])) )
		{
			MARK_FILE(ALL_FILE,flags);
		}else if(!strcmp("-A",argv[i]))
		{
			DEL_FILE(DOT_FILE,flags);
		}else if(!strcmp("-d",argv[i]))
		{
			MARK_FILE(DIR_FILE,flags);
		}else if(!strcmp("-D",argv[i]))
		{
			DEL_FILE(DIR_FILE,flags);
		}else if(!strcmp("-r",argv[i]))
		{
			MARK_FILE(REG_FILE,flags);
		}else if(!strcmp("-R",argv[i]))
		{
			DEL_FILE(REG_FILE,flags);
		}else if(!strcmp("-o",argv[i]))
		{
			MARK_FILE(OTH_FILE,flags);
		}else if(!strcmp("-O",argv[i]))
		{
			DEL_FILE(OTH_FILE,flags);
		}else if(!strcmp("-B",argv[i]))
		{
			DEL_FILE(BAK_FILE,flags);
		}else if(!strcmp("-l",argv[i])
		{
			MARK_INFO(STAT_ALL_INFO,flags);
		}else if(!strcmp("-g",argv[i]))
		{
			MARK_INFO(STAT_GROUP,flags);
			DEL_INFO(STAT_GROUP,flags);
		}else if(!strcmp("-G",argv[i]))
		{
			DEL_INFO(STAT_GROUP,flags);
		}else if(!strcmp("-u",argv[i]))
		{
			MARK_INFO(STAT_OWNER,flags);
			MARK_INFO_DEL(STAT_NUMID,flags);
		}else if(!strcmp("-U",argv[i]))
		{
			DEL_INFO(STAT_OWNER,flags);
		}else if(!strcmp("-n",argv[i]))
		{
			MARK_INFO(STAT_NUMID,flags);
			DEL_INFO(STAT_OWNER,flags);
			DEL_INFO(STAT_GROUP,flags);
		}else if(!strcmp("-i",argv[i]))
		{
			MARK_INFO(STAT_INODE,flags);
		}else if(!strcmp("-I",argv[i]))
		{
			DEL_INFO(STAT_INODE,flags);
		}else if(!strcmp("-t",argv[i]))
		{
			MARK_INFO(STAT_TIME,flags);
		}else if(!strcmp("-T",argv[i]))
		{
			DEL_INFO(STAT_TIME,flags);
		}else if(!strmp("-c",argv[i]))
		{
			MARK(STAT_COLOR,flags);
		}else if(!strcmp("-rec",argv[i])
		{
			MARK(STAT_RECUR,flags);
		}else if(!strcmp("-h",argv[i]))
		{
			MARK(STAT_HR,flags);
		}
		else 
		{
			file_index[files++] = i;
		}
	}
	for(int j=0;i<files;j++)
	{
		do_ls(argv[file_index[i]],flags);
	}
}

void showfile(char *name ,stat *st,int flags)
{
	if( (name[strlen(name)-1]=='~') && (!(BAK_FILE&flags)))
		return ;
	if( (name[0]=='.') && (!(DOT_FILE&flags))
		return;
		
	if( S_ISDIR(st->st_mode) && (!(DIR_FILE&flags)))
		return;
	if( S_ISREG(st->st_mode) && (!(REG_FILE&flags)))
		return;
	
	if((flags & STAT_INFO)
	{
		format_permission(st);
		printf("%3d  ",st->st_nlink)

	}
	if(flags & STAT_OWNER)
		format_owner(st);
	if(flags & STAT_GROUP)
		format_group(st);
	if(flags & STAT_NUMID)
		format_numid(st);
	if(flags & STAT_TIME)
		format_time(st);
		
	if(flags & STAT_SIZE)
		printf("%-15d   ",st->st_size);
	else
		printf("%-10dk   ",st->st_size/1024 +1);
	
	if(flags &STAT_INODE)
		printf("%-15d  ",st->st_inode);
	if(flags &STAT_TIME)
		format_time(st);
	printf("%s\n",name);
}

void do_ls(char *filename,int flags)
{
	DIR *dir;
	char parent_dir[100];
	struct dirent *direntp;
	struct stat st;
	
	if((dir = opendir(filename))==NULL)
	{
		perror("open dir error");
		goto OPENDIR_ERROR;
	}
	if(-1 ==getcwd(parent_dir,100))
	{	
		perror("get parent dir error");
		goto GETCURDIR_ERROR;
	}
	if(-1 == chdir(filename))
	{
		perror("change dir error");
		goto CHDIR_ERROR;
	}
	
	while( (direntp = readdir(dir)) != NULL)
	{
		if(-1 == stat(direntp->d_name,&st))
			continue;
		showfile(direntp->d_name,&st,flags);
		if(S_ISDIR(st.st_mode) && (flags&STAT_RECUR) )
		{
			do_ls(direntp->d_name,flags);
		}
	}
	
	if(-1 == chdir(parent_dir))
	{
		perror("go back to parent error");
		goto CHDIR_ERROR;
	}

CHDIR_ERROR:
GETCURDIR_ERROR:
	closedir(dir);
OPENDIR_ERROR:
}

